package com.mspbots.ticketai.graderule;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.ticketai.GradeRuleInterceptorAdapter;
import com.mspbots.ticketai.model.Ticket;
import com.mspbots.ticketai.model.TicketRule;
import com.mspbots.ticketai.model.TicketSchedule;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


@Log4j2
@Service(value = "SCHEDULED_TIME_POINT_RULE")
public class ScheduleTimeRuleInterceptor extends GradeRuleInterceptorAdapter {

    private List<ScheduleParam> params;

    @Override
    public void prepare(TicketRule rule) {
        params = this.objectMapper.convertValue(rule.getParams(), new TypeReference<>() {
        });
    }

    @Override
    public void process(Ticket ticket) {
        AtomicInteger order = new AtomicInteger();
        List<Map<String, LocalDateTime>> times = new ArrayList<>();
        ObjectNode orderDetailNode = ticket.getOrderDetail() == null ? this.objectMapper.createObjectNode() : ticket.getOrderDetail().deepCopy();
        ObjectNode itemsNode = this.objectMapper.createObjectNode();
        LocalDateTime dateStart = null;
        LocalDateTime dateEnd = null;
        if (!StringUtils.isEmpty(ticket.getResources())
                && !StringUtils.isEmpty(ticket.getIdentifier())
                && !CollectionUtils.isEmpty(ticket.getTicketScheduleList())
                && ticket.getResources().contains(ticket.getIdentifier())) {
            for (TicketSchedule sc : ticket.getTicketScheduleList()) {
                if (!Arrays.stream(StringUtils.commaDelimitedListToStringArray(sc.getIdentifier()))
                        .collect(Collectors.toList()).contains(ticket.getIdentifier())) {
                    continue;
                }
                for (ScheduleParam item : params) {
                    if (!item.isSelected() || !sc.getStatus().equalsIgnoreCase(item.getScheduleType())
                            || item.getMinMinutes() == null || item.getMaxMinutes() == null) {
                        continue;
                    }
                    Long time = 0L;
                    boolean flag = false;
                    if ("startTime".equalsIgnoreCase(item.getTimeType()) && sc.getDateStart() != null) {
                        if ("before".equalsIgnoreCase(item.getCompareType())) {
                            time = Duration.between(LocalDateTime.now(), sc.getDateStart()).toMinutes();
                        } else if ("after".equalsIgnoreCase(item.getCompareType())) {
                            time = Duration.between(sc.getDateStart(), LocalDateTime.now()).toMinutes();
                        }
                        flag = true;
                    } else if ("endTime".equalsIgnoreCase(item.getTimeType()) && sc.getDateEnd() != null) {
                        if ("before".equalsIgnoreCase(item.getCompareType())) {
                            time = Duration.between(LocalDateTime.now(), sc.getDateEnd()).toMinutes();
                        } else if ("after".equalsIgnoreCase(item.getCompareType())) {
                            time = Duration.between(sc.getDateEnd(), LocalDateTime.now()).toMinutes();
                        }
                        flag = true;
                    }
                    if (flag && item.getMinMinutes() <= time && item.getMaxMinutes() >= time) {
                        order.addAndGet(item.getPoints());
                        if (ObjectUtils.isEmpty(dateStart) || dateStart.isAfter(sc.getDateStart())) {
                            dateStart = sc.getDateStart();
                            dateEnd = sc.getDateEnd();
                        }
                        break;
                    }
                }
            }
        }
        if (!ObjectUtils.isEmpty(dateStart)) {
            times.add(Map.of("startTime", dateStart,
                    "endTime", ObjectUtils.isEmpty(dateEnd) ? null : dateEnd));
        }
        itemsNode.putPOJO("scheduleTime", times);
        itemsNode.put("point", order.get());
        orderDetailNode.set("schedule", itemsNode);
        ticket.setOrderDetail(orderDetailNode);
        ticket.setOrder(ticket.getOrder() + order.get());
    }

    @Data
    private static class ScheduleParam {
        private Integer index;
        private Integer points;
        private boolean selected;
        private String scheduleType;
        private String timeType;
        private String compareType;
        private Long minMinutes;
        private Long maxMinutes;
    }
}
